#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OpenBMC ABI提取工具

功能：
1. 从.so文件中提取导出符号
2. 解析C++符号名称（demangle）
3. 分析符号类型（函数/变量/类）
4. 生成Excel表格和JSON数据

使用方法：
    python3 abi_extractor.py --library libsdbusplus.so.1.0.0 --output abi_data.xlsx
"""

import subprocess
import json
import re
import os
import sys
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, asdict
from datetime import datetime

try:
    import pandas as pd
    import openpyxl
    from openpyxl.styles import Font, PatternFill, Alignment
    from openpyxl.utils.dataframe import dataframe_to_rows
    HAS_EXCEL = True
except ImportError:
    HAS_EXCEL = False
    print("[WARNING] pandas或openpyxl未安装，将仅输出JSON格式")


# 符号类型定义
SYMBOL_TYPE_FUNCTION = "function"
SYMBOL_TYPE_OBJECT = "object"
SYMBOL_TYPE_WEAK = "weak"
SYMBOL_TYPE_UNDEFINED = "undefined"
SYMBOL_TYPE_UNKNOWN = "unknown"

# 符号绑定类型
BINDING_GLOBAL = "global"
BINDING_LOCAL = "local"
BINDING_WEAK = "weak"


@dataclass
class Symbol:
    """符号信息数据结构"""
    name: str                      # 原始符号名（mangled）
    demangled_name: str           # 解析后的名称
    address: str                  # 符号地址
    symbol_type: str              # 符号类型（function/object等）
    binding: str                  # 绑定类型（global/local/weak）
    size: int                     # 符号大小（字节）
    section: str                  # 所在section
    visibility: str               # 可见性
    
    # C++特定信息（如果是C++符号）
    namespace: Optional[str] = None      # 命名空间
    class_name: Optional[str] = None     # 类名
    function_name: Optional[str] = None  # 函数名
    is_constructor: bool = False         # 是否为构造函数
    is_destructor: bool = False          # 是否为析构函数
    is_template: bool = False            # 是否为模板
    is_const: bool = False               # 是否为const成员函数
    is_static: bool = False              # 是否为静态函数


@dataclass
class LibraryInfo:
    """库文件信息"""
    name: str                    # 库文件名
    path: str                    # 文件路径
    size: int                    # 文件大小
    architecture: str            # 架构（ARM/x86等）
    format: str                  # 文件格式（ELF等）
    stripped: bool               # 是否已strip
    has_debug_info: bool         # 是否包含调试信息
    build_id: Optional[str]      # Build ID
    dependencies: List[str]      # 依赖的库
    extracted_at: str            # 提取时间


@dataclass
class ABIData:
    """完整的ABI数据"""
    library: LibraryInfo
    symbols: List[Symbol]
    statistics: Dict[str, int]


class ABIExtractor:
    """ABI提取器"""
    
    def __init__(self, library_path: str):
        """
        初始化ABI提取器
        
        参数:
            library_path: 库文件路径
        """
        self.library_path = Path(library_path)
        if not self.library_path.exists():
            raise FileNotFoundError(f"库文件不存在: {library_path}")
        
        self.library_info = None
        self.symbols = []
    
    def extract(self) -> ABIData:
        """
        执行完整的ABI提取流程
        
        返回:
            ABIData对象，包含库信息和所有符号
        """
        print(f"[INFO] 开始提取ABI: {self.library_path.name}")
        
        # 1. 提取库基本信息
        print("[INFO] 步骤1: 提取库基本信息...")
        self.library_info = self._extract_library_info()
        
        # 2. 提取符号表
        print("[INFO] 步骤2: 提取符号表...")
        self.symbols = self._extract_symbols()
        
        # 3. 解析C++符号
        print("[INFO] 步骤3: 解析C++符号...")
        self._demangle_symbols()
        
        # 4. 分析符号特征
        print("[INFO] 步骤4: 分析符号特征...")
        self._analyze_symbols()
        
        # 5. 生成统计信息
        print("[INFO] 步骤5: 生成统计信息...")
        statistics = self._generate_statistics()
        
        print(f"[SUCCESS] 提取完成！共 {len(self.symbols)} 个符号")
        
        return ABIData(
            library=self.library_info,
            symbols=self.symbols,
            statistics=statistics
        )
    
    def _extract_library_info(self) -> LibraryInfo:
        """提取库文件的基本信息"""
        # 使用file命令获取文件类型
        file_output = self._run_command(['file', str(self.library_path)])
        
        # 尝试使用readelf（Linux）或objdump（macOS兼容）
        readelf_output = ""
        try:
            readelf_output = self._run_command(['readelf', '-h', str(self.library_path)])
        except:
            # macOS上使用objdump
            try:
                readelf_output = self._run_command(['objdump', '-h', str(self.library_path)])
            except:
                pass
        
        # 解析架构
        arch = "unknown"
        if "ARM" in file_output:
            arch = "ARM"
        elif "x86-64" in file_output or "x86_64" in file_output:
            arch = "x86_64"
        elif "Intel 80386" in file_output:
            arch = "x86"
        
        # 解析是否stripped
        stripped = "not stripped" not in file_output
        has_debug = "with debug_info" in file_output
        
        # 提取Build ID
        build_id = None
        build_id_match = re.search(r'BuildID\[sha1\]=([0-9a-f]+)', file_output)
        if build_id_match:
            build_id = build_id_match.group(1)
        
        # 获取依赖库（使用readelf或otool）
        dependencies = []
        try:
            # 尝试readelf（Linux）
            deps_output = self._run_command(['readelf', '-d', str(self.library_path)])
            for line in deps_output.split('\n'):
                if 'NEEDED' in line:
                    match = re.search(r'\[([^\]]+)\]', line)
                    if match:
                        dependencies.append(match.group(1))
        except:
            # macOS上使用otool
            try:
                deps_output = self._run_command(['otool', '-L', str(self.library_path)])
                for line in deps_output.split('\n')[1:]:  # 跳过第一行（文件名）
                    match = re.search(r'\s+([^\s]+)', line)
                    if match:
                        dep_path = match.group(1)
                        dependencies.append(os.path.basename(dep_path))
            except:
                pass
        
        return LibraryInfo(
            name=self.library_path.name,
            path=str(self.library_path.absolute()),
            size=self.library_path.stat().st_size,
            architecture=arch,
            format="ELF",
            stripped=stripped,
            has_debug_info=has_debug,
            build_id=build_id,
            dependencies=dependencies,
            extracted_at=datetime.now().isoformat()
        )
    
    def _extract_symbols(self) -> List[Symbol]:
        """使用nm提取符号表"""
        symbols = []
        
        # 使用nm -D提取动态符号
        nm_output = ""
        try:
            # 先尝试带--print-size（GNU nm）
            nm_output = self._run_command(['nm', '-D', '--defined-only', 
                                          '--print-size', str(self.library_path)])
        except:
            try:
                # macOS nm可能不支持某些选项
                nm_output = self._run_command(['nm', '-gD', str(self.library_path)])
            except:
                # 最后尝试最简单的形式
                nm_output = self._run_command(['nm', '-D', str(self.library_path)])
        
        lines = nm_output.split('\n')
        
        for line in lines:
            if not line.strip():
                continue
            
            symbol = self._parse_nm_line(line)
            if symbol:
                symbols.append(symbol)
        
        return symbols
    
    def _parse_nm_line(self, line: str) -> Optional[Symbol]:
        """
        解析nm输出的一行
        
        nm输出格式:
        00012178 00000004 B _ZGVN5boost...  (with --print-size)
        0000f760 T _ZN9sdbusplus3bus10new_systemEv
                 U _Unwind_Resume@GCC_3.0
        """
        # nm --print-size的输出格式: 地址 大小 类型 符号名
        # nm 普通格式: 地址 类型 符号名
        
        # 先尝试匹配带大小的格式
        pattern_with_size = r'^\s*([0-9a-f]{8,16})\s+([0-9a-f]{8})\s+([A-Za-z])\s+(.+)$'
        match = re.match(pattern_with_size, line)
        
        if match:
            address = match.group(1)
            size_hex = match.group(2)
            type_char = match.group(3)
            name = match.group(4).strip()
            size = int(size_hex, 16) if size_hex else 0
        else:
            # 尝试不带大小的格式
            pattern = r'^\s*([0-9a-f]{8,16})?\s+([A-Za-z])\s+(.+)$'
            match = re.match(pattern, line)
            
            if not match:
                return None
            
            address = match.group(1) or ""
            type_char = match.group(2)
            name = match.group(3).strip()
            size = 0
        
        # 过滤空名称
        if not name:
            return None
        
        # 处理符号版本（如@GCC_3.0）
        if '@' in name:
            name = name.split('@')[0]
        
        # 解析符号类型
        symbol_type, binding = self._parse_symbol_type(type_char)
        
        return Symbol(
            name=name,
            demangled_name="",  # 后续填充
            address=address,
            symbol_type=symbol_type,
            binding=binding,
            size=size,
            section="",
            visibility="default"
        )
    
    def _parse_symbol_type(self, type_char: str) -> Tuple[str, str]:
        """
        解析nm的符号类型字符
        
        类型字符说明:
        T/t - 函数（代码段）
        D/d - 已初始化数据
        B/b - 未初始化数据（BSS）
        R/r - 只读数据
        W/w - 弱符号
        U - 未定义（外部引用）
        """
        type_map = {
            'T': (SYMBOL_TYPE_FUNCTION, BINDING_GLOBAL),
            't': (SYMBOL_TYPE_FUNCTION, BINDING_LOCAL),
            'D': (SYMBOL_TYPE_OBJECT, BINDING_GLOBAL),
            'd': (SYMBOL_TYPE_OBJECT, BINDING_LOCAL),
            'B': (SYMBOL_TYPE_OBJECT, BINDING_GLOBAL),
            'b': (SYMBOL_TYPE_OBJECT, BINDING_LOCAL),
            'R': (SYMBOL_TYPE_OBJECT, BINDING_GLOBAL),
            'r': (SYMBOL_TYPE_OBJECT, BINDING_LOCAL),
            'W': (SYMBOL_TYPE_WEAK, BINDING_WEAK),
            'w': (SYMBOL_TYPE_WEAK, BINDING_WEAK),
            'U': (SYMBOL_TYPE_UNDEFINED, BINDING_GLOBAL),
        }
        
        return type_map.get(type_char, (SYMBOL_TYPE_UNKNOWN, BINDING_GLOBAL))
    
    def _demangle_symbols(self):
        """使用c++filt解析C++符号名称"""
        # 收集所有需要demangle的符号
        mangled_names = [s.name for s in self.symbols if s.name.startswith('_Z')]
        
        if not mangled_names:
            return
        
        # 批量demangle
        try:
            input_text = '\n'.join(mangled_names)
            result = subprocess.run(
                ['c++filt'],
                input=input_text,
                capture_output=True,
                text=True,
                check=True
            )
            
            demangled_names = result.stdout.strip().split('\n')
            
            # 更新符号的demangled_name
            name_index = 0
            for symbol in self.symbols:
                if symbol.name.startswith('_Z'):
                    if name_index < len(demangled_names):
                        symbol.demangled_name = demangled_names[name_index]
                        name_index += 1
                else:
                    symbol.demangled_name = symbol.name
        except Exception as e:
            print(f"[WARNING] C++符号解析失败: {e}")
            # 如果解析失败，使用原始名称
            for symbol in self.symbols:
                if not symbol.demangled_name:
                    symbol.demangled_name = symbol.name
    
    def _analyze_symbols(self):
        """分析符号的C++特征"""
        for symbol in self.symbols:
            if symbol.symbol_type != SYMBOL_TYPE_FUNCTION:
                continue
            
            demangled = symbol.demangled_name
            
            # 提取命名空间和类名
            # 使用改进的方法，正确处理模板参数
            self._extract_cpp_info(symbol, demangled)
            
            # 判断是否为模板
            symbol.is_template = '<' in demangled and '>' in demangled
            
            # 判断是否为const成员函数
            symbol.is_const = ' const' in demangled or ') const' in demangled
    
    def _extract_cpp_info(self, symbol: Symbol, demangled: str):
        """
        提取C++符号的命名空间、类名、函数名
        正确处理模板参数中的::
        """
        # 找到第一个左括号（函数参数开始）
        paren_idx = demangled.find('(')
        if paren_idx == -1:
            # 没有括号，可能是变量
            return
        
        # 提取函数签名部分（括号之前）
        signature = demangled[:paren_idx]
        
        # 移除返回值类型（如果有）
        # 返回值类型通常在最前面，后面跟空格
        parts_before_func = signature.split(' ')
        if len(parts_before_func) > 1:
            # 最后一个部分是函数路径
            func_path = parts_before_func[-1]
        else:
            func_path = signature
        
        # 现在处理func_path，需要正确分割::但忽略模板内的::
        path_parts = self._split_cpp_path(func_path)
        
        if not path_parts:
            return
        
        # 最后一个是函数名
        symbol.function_name = path_parts[-1]
        
        # 如果有多个部分，倒数第二个可能是类名
        if len(path_parts) >= 2:
            symbol.class_name = path_parts[-2]
            
            # 判断构造/析构函数
            if symbol.function_name == symbol.class_name:
                symbol.is_constructor = True
            elif symbol.function_name == '~' + symbol.class_name:
                symbol.is_destructor = True
        
        # 如果有3个或更多部分，前面的是命名空间
        if len(path_parts) >= 3:
            symbol.namespace = '::'.join(path_parts[:-2])
        elif len(path_parts) == 2:
            # 只有两部分，第一部分是命名空间
            symbol.namespace = path_parts[0]
    
    def _split_cpp_path(self, path: str) -> List[str]:
        """
        分割C++路径，正确处理模板参数中的::
        例如: "std::vector<std::string>::push_back" -> ["std", "vector<std::string>", "push_back"]
        """
        parts = []
        current = ""
        template_depth = 0
        
        i = 0
        while i < len(path):
            char = path[i]
            
            if char == '<':
                template_depth += 1
                current += char
            elif char == '>':
                template_depth -= 1
                current += char
            elif char == ':' and i + 1 < len(path) and path[i + 1] == ':':
                # 遇到::
                if template_depth == 0:
                    # 不在模板内，这是真正的分隔符
                    if current:
                        parts.append(current)
                    current = ""
                    i += 1  # 跳过第二个:
                else:
                    # 在模板内，保留::
                    current += '::'
                    i += 1
            else:
                current += char
            
            i += 1
        
        # 添加最后一部分
        if current:
            parts.append(current)
        
        return parts
    
    def _generate_statistics(self) -> Dict[str, int]:
        """生成统计信息"""
        stats = {
            'total_symbols': len(self.symbols),
            'functions': 0,
            'objects': 0,
            'weak_symbols': 0,
            'undefined_symbols': 0,
            'cpp_symbols': 0,
            'c_symbols': 0,
            'namespaces': set(),
            'classes': set(),
            'constructors': 0,
            'destructors': 0,
            'templates': 0,
            'const_functions': 0,
        }
        
        for symbol in self.symbols:
            # 统计符号类型
            if symbol.symbol_type == SYMBOL_TYPE_FUNCTION:
                stats['functions'] += 1
            elif symbol.symbol_type == SYMBOL_TYPE_OBJECT:
                stats['objects'] += 1
            elif symbol.symbol_type == SYMBOL_TYPE_WEAK:
                stats['weak_symbols'] += 1
            elif symbol.symbol_type == SYMBOL_TYPE_UNDEFINED:
                stats['undefined_symbols'] += 1
            
            # 统计C++符号
            if symbol.name.startswith('_Z'):
                stats['cpp_symbols'] += 1
            else:
                stats['c_symbols'] += 1
            
            # 统计C++特征
            if symbol.namespace:
                stats['namespaces'].add(symbol.namespace)
            if symbol.class_name:
                stats['classes'].add(symbol.class_name)
            if symbol.is_constructor:
                stats['constructors'] += 1
            if symbol.is_destructor:
                stats['destructors'] += 1
            if symbol.is_template:
                stats['templates'] += 1
            if symbol.is_const:
                stats['const_functions'] += 1
        
        # 转换set为数量
        stats['unique_namespaces'] = len(stats['namespaces'])
        stats['unique_classes'] = len(stats['classes'])
        del stats['namespaces']
        del stats['classes']
        
        return stats
    
    def _run_command(self, cmd: List[str]) -> str:
        """
        执行shell命令并返回输出
        
        参数:
            cmd: 命令列表
        
        返回:
            命令输出字符串
        """
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=True
            )
            return result.stdout
        except subprocess.CalledProcessError as e:
            raise RuntimeError(f"命令执行失败: {' '.join(cmd)}\n{e.stderr}")
    
    def save_to_excel(self, output_path: str, abi_data: ABIData):
        """
        保存ABI数据到Excel文件
        
        参数:
            output_path: 输出文件路径（.xlsx）
            abi_data: ABI数据对象
        """
        if not HAS_EXCEL:
            print("[ERROR] 需要安装pandas和openpyxl: pip install pandas openpyxl")
            return self.save_to_json(output_path.replace('.xlsx', '.json'), abi_data)
        
        output_file = Path(output_path)
        output_file.parent.mkdir(parents=True, exist_ok=True)
        
        # 创建Excel写入器
        with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
            # Sheet 1: 库信息
            lib_df = pd.DataFrame([{
                '库文件名': abi_data.library.name,
                '文件路径': abi_data.library.path,
                '文件大小(字节)': abi_data.library.size,
                '架构': abi_data.library.architecture,
                '格式': abi_data.library.format,
                '是否stripped': '是' if abi_data.library.stripped else '否',
                '包含调试信息': '是' if abi_data.library.has_debug_info else '否',
                'Build ID': abi_data.library.build_id or 'N/A',
                '提取时间': abi_data.library.extracted_at,
            }])
            lib_df.to_excel(writer, sheet_name='库信息', index=False)
            
            # Sheet 2: 依赖库
            if abi_data.library.dependencies:
                deps_df = pd.DataFrame({
                    '序号': range(1, len(abi_data.library.dependencies) + 1),
                    '依赖库': abi_data.library.dependencies
                })
                deps_df.to_excel(writer, sheet_name='依赖库', index=False)
            
            # Sheet 3: 统计信息
            stats_df = pd.DataFrame([{
                '指标': k,
                '数值': v
            } for k, v in abi_data.statistics.items()])
            stats_df.to_excel(writer, sheet_name='统计信息', index=False)
            
            # Sheet 4: 符号列表（完整）
            symbols_data = []
            for idx, symbol in enumerate(abi_data.symbols, 1):
                symbols_data.append({
                    '序号': idx,
                    '符号名称': symbol.name,
                    '解析后名称': symbol.demangled_name,
                    '地址': symbol.address,
                    '类型': symbol.symbol_type,
                    '绑定': symbol.binding,
                    '大小': symbol.size,
                    '命名空间': symbol.namespace or '',
                    '类名': symbol.class_name or '',
                    '函数名': symbol.function_name or '',
                    '构造函数': '是' if symbol.is_constructor else '',
                    '析构函数': '是' if symbol.is_destructor else '',
                    '模板': '是' if symbol.is_template else '',
                    'const函数': '是' if symbol.is_const else '',
                })
            
            symbols_df = pd.DataFrame(symbols_data)
            symbols_df.to_excel(writer, sheet_name='符号列表', index=False)
            
            # Sheet 5: 函数列表（仅函数）
            functions_data = [s for s in symbols_data 
                            if abi_data.symbols[s['序号']-1].symbol_type == SYMBOL_TYPE_FUNCTION]
            if functions_data:
                functions_df = pd.DataFrame(functions_data)
                functions_df.to_excel(writer, sheet_name='函数列表', index=False)
            
            # Sheet 6: C++类列表
            classes = {}
            for symbol in abi_data.symbols:
                if symbol.class_name:
                    if symbol.class_name not in classes:
                        classes[symbol.class_name] = {
                            '类名': symbol.class_name,
                            '命名空间': symbol.namespace or '',
                            '方法数量': 0,
                            '构造函数': 0,
                            '析构函数': 0,
                        }
                    classes[symbol.class_name]['方法数量'] += 1
                    if symbol.is_constructor:
                        classes[symbol.class_name]['构造函数'] += 1
                    if symbol.is_destructor:
                        classes[symbol.class_name]['析构函数'] += 1
            
            if classes:
                classes_df = pd.DataFrame(list(classes.values()))
                classes_df.to_excel(writer, sheet_name='C++类列表', index=False)
        
        # 美化Excel
        self._format_excel(output_file)
        
        print(f"[SUCCESS] ABI数据已保存到Excel: {output_path}")
        
        # 同时保存JSON格式
        json_path = output_file.with_suffix('.json')
        self.save_to_json(str(json_path), abi_data)
    
    def save_to_json(self, output_path: str, abi_data: ABIData):
        """
        保存ABI数据到JSON文件
        
        参数:
            output_path: 输出文件路径
            abi_data: ABI数据对象
        """
        output_file = Path(output_path)
        output_file.parent.mkdir(parents=True, exist_ok=True)
        
        # 转换为字典
        data = {
            'library': asdict(abi_data.library),
            'symbols': [asdict(s) for s in abi_data.symbols],
            'statistics': abi_data.statistics
        }
        
        # 写入JSON文件
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        
        print(f"[SUCCESS] ABI数据已保存到JSON: {output_path}")
        
        # 同时保存一个人类可读的摘要
        summary_path = output_file.with_suffix('.summary.txt')
        self._save_summary(summary_path, abi_data)
    
    def _format_excel(self, excel_path: Path):
        """美化Excel表格"""
        wb = openpyxl.load_workbook(excel_path)
        
        # 设置表头样式
        header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
        header_font = Font(bold=True, color="FFFFFF")
        
        for sheet in wb.worksheets:
            # 设置表头样式
            for cell in sheet[1]:
                cell.fill = header_fill
                cell.font = header_font
                cell.alignment = Alignment(horizontal='center', vertical='center')
            
            # 自动调整列宽
            for column in sheet.columns:
                max_length = 0
                column_letter = column[0].column_letter
                for cell in column:
                    try:
                        if cell.value:
                            max_length = max(max_length, len(str(cell.value)))
                    except:
                        pass
                adjusted_width = min(max_length + 2, 60)  # 最大60字符宽度
                sheet.column_dimensions[column_letter].width = adjusted_width
        
        wb.save(excel_path)
    
    def _save_summary(self, summary_path: Path, abi_data: ABIData):
        """保存人类可读的摘要文件"""
        with open(summary_path, 'w', encoding='utf-8') as f:
            f.write(f"# ABI提取摘要 - {abi_data.library.name}\n")
            f.write(f"提取时间: {abi_data.library.extracted_at}\n")
            f.write(f"=" * 80 + "\n\n")
            
            # 库信息
            f.write("## 库信息\n")
            f.write(f"文件名: {abi_data.library.name}\n")
            f.write(f"大小: {abi_data.library.size:,} 字节\n")
            f.write(f"架构: {abi_data.library.architecture}\n")
            f.write(f"Build ID: {abi_data.library.build_id}\n")
            f.write(f"调试信息: {'是' if abi_data.library.has_debug_info else '否'}\n")
            f.write(f"已strip: {'是' if abi_data.library.stripped else '否'}\n")
            f.write(f"\n依赖库:\n")
            for dep in abi_data.library.dependencies:
                f.write(f"  - {dep}\n")
            f.write("\n")
            
            # 统计信息
            f.write("## 统计信息\n")
            for key, value in abi_data.statistics.items():
                f.write(f"{key}: {value}\n")
            f.write("\n")
            
            # 符号列表（前50个）
            f.write("## 符号列表（前50个）\n")
            f.write(f"{'地址':<12} {'类型':<8} {'绑定':<8} {'解析后的名称'}\n")
            f.write("-" * 80 + "\n")
            for symbol in abi_data.symbols[:50]:
                f.write(f"{symbol.address:<12} {symbol.symbol_type:<8} "
                       f"{symbol.binding:<8} {symbol.demangled_name}\n")
            
            if len(abi_data.symbols) > 50:
                f.write(f"\n... 还有 {len(abi_data.symbols) - 50} 个符号\n")
        
        print(f"[INFO] 摘要已保存到: {summary_path}")


def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(
        description='OpenBMC ABI提取工具',
        formatter_class=argparse.RawDescriptionHelpFormatter
    )
    parser.add_argument(
        '--library',
        required=True,
        help='库文件路径'
    )
    parser.add_argument(
        '--output',
        required=True,
        help='输出JSON文件路径'
    )
    parser.add_argument(
        '--verbose',
        action='store_true',
        help='显示详细信息'
    )
    
    args = parser.parse_args()
    
    try:
        # 创建提取器
        extractor = ABIExtractor(args.library)
        
        # 执行提取
        abi_data = extractor.extract()
        
        # 保存结果（根据文件扩展名决定格式）
        if args.output.endswith('.xlsx'):
            extractor.save_to_excel(args.output, abi_data)
        else:
            extractor.save_to_json(args.output, abi_data)
        
        # 显示摘要
        print("\n" + "=" * 80)
        print("提取摘要:")
        print(f"  库文件: {abi_data.library.name}")
        print(f"  大小: {abi_data.library.size:,} 字节")
        print(f"  符号总数: {abi_data.statistics['total_symbols']}")
        print(f"  函数: {abi_data.statistics['functions']}")
        print(f"  C++符号: {abi_data.statistics['cpp_symbols']}")
        print(f"  命名空间: {abi_data.statistics['unique_namespaces']}")
        print(f"  类: {abi_data.statistics['unique_classes']}")
        print("=" * 80)
        
        return 0
        
    except Exception as e:
        print(f"[ERROR] 提取失败: {e}", file=sys.stderr)
        if args.verbose:
            import traceback
            traceback.print_exc()
        return 1


if __name__ == '__main__':
    sys.exit(main())

